/*
* Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
*
* Oracle is a registered trademarks of Oracle Corporation and/or its affiliates.
*
* This software is the confidential and proprietary information of
* Oracle Corporation.  You shall not disclose such confidential and
* proprietary information and shall use it only in accordance with the
* terms of the license agreement you entered into with Oracle.
*
* This notice may not be removed or altered.
*/
using System;
using System.Collections;
using System.Windows.Forms;

using Tangosol.Net;
using Tangosol.Net.Cache;
using Tangosol.Net.Cache.Support;
using Tangosol.Util;
using Tangosol.Util.Extractor;
using Tangosol.Util.Filter;

namespace Examples
{
    /// <summary>
    /// A simple windows form that demonstrates various
    /// Coherence.NET features.
    /// </summary>
    public partial class ContactForm : Form
    {
        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public ContactForm()
        {
            listener = new WindowsFormsCacheListener(this);
            listener.EntryInserted += new CacheEventHandler(AddRow);
            listener.EntryUpdated  += new CacheEventHandler(UpdateRow);
            listener.EntryDeleted  += new CacheEventHandler(DeleteRow);

            cache = CacheFactory.GetCache("dist-contact-cache");
            cache.AddCacheListener(listener);

            InitializeComponent();
            InitializeComboBox();
        }

        #endregion

        #region Helper methods

        /// <summary>
        /// Resets all of the text boxes on the form.
        /// </summary>
        private void ResetTextBoxes()
        {
            txtName.Text   = "";
            txtStreet.Text = "";
            txtCity.Text   = "";
            txtState.Text  = "";
            txtZip.Text    = "";
        }

        /// <summary>
        /// Initialize <b>ComboBox</b> with attribute names.
        /// </summary>
        /// <remarks>
        /// Choosing attribute from the ComboBox allows to search for given
        /// pattern in choosen entry attribute inside the named cache.
        /// </remarks>
        private void InitializeComboBox()
        {
            cmbAttribute.Items.Add("Name");
            cmbAttribute.Items.Add("Street");
            cmbAttribute.Items.Add("City");
            cmbAttribute.Items.Add("State");
            cmbAttribute.Items.Add("Zip");

            cmbAttribute.SelectedIndex = 0;
        }

        /// <summary>
        /// Refreshes the contacts table.
        /// </summary>
        /// <param name="updateContacts">
        /// Flag specifying whether to query against cache to get
        /// the most recent data or not.
        /// </param>
        private void RefreshContactsGrid(bool updateContacts)
        {
            if (updateContacts)
            {
                RefreshContacts();
            }
            contactsBindingSource.ResetBindings(false);
        }

        /// <summary>
        /// Refreshes the contacts table with the most recent data within the
        /// cache.
        /// </summary>
        private void RefreshContacts()
        {
            contactsBindingSource.Clear();
            ICollection cacheValues = cache.GetValues(filter);
            foreach (object contact in cacheValues)
            {
                contactsBindingSource.Add(contact);
            }
        }

        #endregion

        #region Form event handlers

        /// <summary>
        /// Load form event handler.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void ContactForm_Load(object sender, EventArgs e)
        {
            RefreshContactsGrid(true);
        }

        /// <summary>
        /// Closed form event handler.
        /// </summary>
        /// <remarks>
        /// Removes the event handlers.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void ContactForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            cache.RemoveCacheListener(listener, cacheEventFilter);
        }

        /// <summary>
        /// Enter cell event handler for the <b>ContactDataGridView</b>.
        /// </summary>
        /// <remarks>
        /// Refreshes the <b>TextBox</b>es with data from selected
        /// <b>ContactDataGridView</b> row.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void addressDataGridView_CellEnter(object sender, DataGridViewCellEventArgs e)
        {
            DataGridViewCellCollection cells = addressDataGridView.CurrentRow.Cells;

            txtName.Text   = (string) cells[0].Value;
            txtStreet.Text = (string) cells[1].Value;
            txtCity.Text   = (string) cells[2].Value;
            txtState.Text  = (string) cells[3].Value;
            txtZip.Text    = (string) cells[4].Value;
        }

        /// <summary>
        /// Click event handler for <b>Put</b> button.
        /// </summary>
        /// <remarks>
        /// Stores the <see cref="ContactInfo"/> data entered in
        /// <b>TextBox</b>es into the INamedCache.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void btnPut_Click(object sender, EventArgs e)
        {
            String name  = txtName.Text;
            ContactInfo contact = new ContactInfo(txtName.Text,
                                                  txtStreet.Text,
                                                  txtCity.Text,
                                                  txtState.Text,
                                                  txtZip.Text);
            cache.Insert(name, contact);
        }

        /// <summary>
        /// Click event handler for <b>Remove</b> button.
        /// </summary>
        /// <remarks>
        /// Removes the <see cref="ContactInfo"/> mapped by the current
        /// Name <b>TextBox</b> value.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void btnRemove_Click(object sender, EventArgs e)
        {
            cache.Remove(txtName.Text);
            ResetTextBoxes();
        }

        /// <summary>
        /// Click event handler for <b>Clear</b> button.
        /// </summary>
        /// <remarks>
        /// Clears the <b>INamedCache</b>.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void btnClear_Click(object sender, EventArgs e)
        {
            cache.RemoveCacheListener(listener, cacheEventFilter);
            cache.Clear();
            cache.AddCacheListener(listener, cacheEventFilter, false);

            contactsBindingSource.Clear();
            ResetTextBoxes();
        }

        /// <summary>
        /// Click event handler for <b>Refresh</b> button.
        /// </summary>
        /// <remarks>
        /// Refreshes the <b>addressDataGridView</b>, filtering named cache
        /// entries by a given attribute and string pattern. If empty string
        /// is provided as a pattern all entries in the named cache will be
        /// accounted and displayed.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void btnRefresh_Click(object sender, EventArgs e)
        {
            string newPattern = txtPattern.Text;
            string attribute = (string) cmbAttribute.SelectedItem;

            if (!newPattern.Equals(pattern))
            {
                pattern = newPattern;
                cache.RemoveCacheListener(listener, cacheEventFilter);

                if (pattern != String.Empty)
                {
                    IValueExtractor extractor = new ReflectionExtractor("get" + attribute);
                    filter = new LikeFilter(extractor, pattern, '\\', false);
                    cacheEventFilter = new CacheEventFilter(CacheEventFilter.CacheEventMask.All
                                                            | CacheEventFilter.CacheEventMask.UpdatedEntered
                                                            | CacheEventFilter.CacheEventMask.UpdatedLeft,
                                                            filter);
                }
                else
                {
                    filter = null;
                    cacheEventFilter = null;
                }
                cache.AddCacheListener(listener, cacheEventFilter, false);
            }

            RefreshContactsGrid(true);
        }

        /// <summary>
        /// Click event handler for <b>SelectIndexChanged</b> event.
        /// </summary>
        /// <remarks>
        /// Resets the pattern string in order to Refresh button click event
        /// handler would work properly.
        /// </remarks>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="e">
        /// An <b>EventArgs</b> that contains no event data.
        /// </param>
        private void cmbAttribute_SelectedIndexChanged(object sender, EventArgs e)
        {
            pattern = "";
        }

        #endregion

        #region Cache event handlers

        /// <summary>
        /// Event handler for  <see cref="ICacheListener.EntryInserted"/>
        /// event.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="args">
        /// An <see cref="CacheEventArgs"/>.
        /// </param>
        private void AddRow(object sender, CacheEventArgs args)
        {
            contactsBindingSource.Add(args.NewValue);
        }

        /// <summary>
        /// Event handler for  <see cref="ICacheListener.EntryUpdated"/>
        /// event.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="args">
        /// An <see cref="CacheEventArgs"/>.
        /// </param>
        public void UpdateRow(object sender, CacheEventArgs args)
        {
            int index = contactsBindingSource.IndexOf(args.OldValue);
            if (index < 0)
            {
                // updated entered
                contactsBindingSource.Add(args.NewValue);
            }
            else
            {
                if (pattern == String.Empty || SatisfiesFilter(args.NewValue))
                {
                    contactsBindingSource[index] = args.NewValue;
                }
                else
                {
                    contactsBindingSource.RemoveAt(index);
                }
            }
        }

        /// <summary>
        /// Queries the object with specified filter criteria.
        /// </summary>
        /// <param name="obj">
        /// An object to which the test is applied.
        /// </param>
        /// <returns>
        /// <b>true</b> if the test passes, <b>false</b> otherwise.
        /// </returns>
        private bool SatisfiesFilter(object obj)
        {
            IFilter clientFilter =
                    new LikeFilter(new ReflectionExtractor((string) cmbAttribute.SelectedItem),
                                   pattern, '\\', false);
            return clientFilter.Evaluate(obj);
        }

        /// <summary>
        /// Event handler for <see cref="ICacheListener.EntryDeleted"/>
        /// event.
        /// </summary>
        /// <param name="sender">
        /// The source of the event.
        /// </param>
        /// <param name="args">
        /// An <see cref="CacheEventArgs"/>.
        /// </param>
        public void DeleteRow(object sender, CacheEventArgs args)
        {
            contactsBindingSource.Remove(args.OldValue);
        }

        #endregion

        #region Data members

        /// <summary>
        /// Named cache.
        /// </summary>
        private INamedCache cache;

        /// <summary>
        /// Listener that allows end users to handle Coherence cache events,
        /// which are always raised from a background thread.
        /// </summary>
        private WindowsFormsCacheListener listener;

        /// <summary>
        /// Evaluate the specified extracted value.
        /// </summary>
        private IFilter filter;

        /// <summary>
        /// Wrapper filter, used by listeners.
        /// </summary>
        private CacheEventFilter cacheEventFilter;

        /// <summary>
        /// Search pattern.
        /// </summary>
        private string pattern;

        #endregion

    }
}